[HNOI2012]矿场搭建

2019-11-12
HNOI

题意

在无向联通图中设置一些庇护所,使得任何1个点被删掉之后(同时在该地的庇护所也失效),所有点(包括删掉的这个点)都能到达至少一个庇护所

求最小的庇护所数及最优的方案数

题解

把所有的割点求出来,把这个图分为一些联通块

如果联通块里面割点个数为0,需要建两个,方案数为\(C_{cnt}^2\)

如果联通块里面割点个数为1,需要在除割点外建1个,方案数为\(cnt-1\)

如果联通块里面割点个数大于1,不论怎样都可以跑到其他联通块中,不用建

调试记录

没写Case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <cstdio>
#include <algorithm>
#include <cstring>
#define LL long long
const int maxn = 505;
using namespace std;
struct E{
int to, nxt;
}e[maxn << 1];
int head[maxn], tot = 0;
void addedge(int u, int v){
e[++tot].to = v, e[tot].nxt = head[u];
head[u] = tot;
}
int n, m, _cnt, tdx;
int low[maxn], dfn[maxn];
bool cut[maxn];
void Tarjan(int cur){
low[cur] = dfn[cur] = ++tdx;
for (int i = head[cur]; i; i = e[i].nxt){
int v = e[i].to;
if (dfn[v]) low[cur] = min(low[cur], dfn[v]);
else{
Tarjan(v); low[cur] = min(low[cur], low[v]);
if (low[v] >= dfn[cur] && cur != 1) cut[cur] = 1;
if (cur == 1) ++_cnt;
}
}
if (cur == 1) cut[cur] = (_cnt > 1);
}
LL ans; int Min, c, t, Col, vis[maxn];
void dfs(int cur, int fa){
vis[cur] = Col;
++t;
for (int i = head[cur]; i; i = e[i].nxt){
int v = e[i].to;
if (v == fa) continue;
if (!vis[v] && !cut[v]) dfs(v, cur);
else if (cut[v] && vis[v] != Col) ++c, vis[v] = Col;
}
}
int T;
int main(){
scanf("%d", &m);
while (m != 0){
memset(head, 0, sizeof head); tot = 0; ans = 1ll, Min = 0; Col = 0; n = 0;
memset(dfn, 0, sizeof dfn); memset(low, 0, sizeof low); _cnt = 0; tdx = 0;
memset(cut, 0, sizeof cut); memset(vis, 0, sizeof vis);
for (int u, v, i = 1; i <= m; i++){
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
n = max(n, max(u, v));
}
Tarjan(1);
for (int i = 1; i <= n; i++)
if (!cut[i] && !vis[i]){
c = 0; t = 0; ++Col;
dfs(i, 0);
if (c == 0) ans *= 1ll * (t - 1) * t / 2, Min += 2;
if (c == 1) ans *= t, Min++;
}
printf("Case %d: %d %lld\n", ++T, Min, ans);
scanf("%d", &m);
}
return 0;
}